home *** CD-ROM | disk | FTP | other *** search
/ Delphi Magazine Collection 2001 / Delphi Magazine Collection 20001 (2001).iso / DISKS / Issue41 / Patterns / HVSingleton.PAS < prev    next >
Encoding:
Pascal/Delphi Source File  |  1998-11-18  |  5.3 KB  |  183 lines

  1. unit HVSingleton;
  2.  
  3. interface
  4.  
  5. uses
  6.   SysUtils;
  7.  
  8. type
  9.   ESingleton = class(Exception);
  10.  
  11.   TInvalidateDestroy = class(TObject)
  12.   protected
  13.     class procedure SingletonError;
  14.   public
  15.     destructor Destroy; override;
  16.   end;
  17.  
  18.   TSingletonOpaqueInfo = record end;
  19.   TSingletonHandle = ^TSingletonOpaqueInfo;
  20.   TSingleton = class;
  21.   TSingletonClass = class of TSingleton;
  22.   TSingleton = class(TInvalidateDestroy)
  23.   private
  24.     class procedure Startup;
  25.     class procedure Shutdown;
  26.   protected
  27.     // Allow descendents register themselves
  28.     class function RegisterSingletonClass(aSingletonClass: TSingletonClass): TSingletonHandle;
  29.     // Allow descendents to set a new class for the instance:
  30.     class procedure OverrideSingletonClass(BaseSingletonClass, NewSingletonClass: TSingletonClass);
  31.     // Interface for descendents to get their instance pointer
  32.     class function InstanceOf(Handle: TSingletonHandle): TSingleton;
  33.     // Actual constructor and destructor that will be used:
  34.     constructor SingletonCreate; virtual;
  35.     destructor SingletonDestroy; virtual;
  36.   public
  37.     // Not for use - for obstruction only:
  38.     class procedure Create;
  39.     class procedure Free(Dummy: integer);
  40. {$IFNDEF VER120} {$WARNINGS OFF} {$ENDIF}
  41.     // This generates a warning in D3. D4 has the reintroduce keyword to solve this
  42.     class procedure Destroy(Dummy: integer); {$IFDEF VER120} reintroduce; {$ENDIF}
  43.   end;
  44. {$IFNDEF VER120} {$WARNINGS ON} {$ENDIF}
  45.  
  46. implementation
  47.  
  48. uses
  49.   Classes;
  50.  
  51. { TInvalidateDestroy }
  52.  
  53. class procedure TInvalidateDestroy.SingletonError;
  54. // Raise an exception in case of illegal use
  55. begin
  56.   raise ESingleton.CreateFmt('Illegal use of %s singleton instance!', [ClassName]);
  57. end;
  58.  
  59. destructor TInvalidateDestroy.Destroy;
  60. // Protected against use of default destructor
  61. begin
  62.   SingletonError;
  63. end;
  64.  
  65. { TSingleton }
  66.  
  67. var
  68.   SingletonInstances : TList; { of TSingletons       }
  69.   SingletonClasses   : TList; { of TSingletonClasses }
  70.  
  71. class procedure TSingleton.Startup;
  72. begin
  73.   SingletonInstances := TList.Create;
  74.   SingletonClasses   := TList.Create;
  75. end;
  76.  
  77. class procedure TSingleton.Shutdown;
  78. // Time to close down the show
  79. var
  80.   SingletonInstance: TSingleton;
  81.   i : integer;
  82. begin
  83.   // Free any singleton instances
  84.   for i := SingletonInstances.Count-1 downto 0 do
  85.   begin
  86.     SingletonInstance := TSingleton(SingletonInstances.List^[i]);
  87.     if Assigned(SingletonInstance) then
  88.       SingletonInstance.SingletonDestroy;
  89.   end;
  90.   // Free the lists
  91.   SingletonInstances.Free; SingletonInstances := nil;
  92.   SingletonClasses  .Free; SingletonClasses   := nil;
  93. end;
  94.  
  95. class function TSingleton.RegisterSingletonClass(aSingletonClass: TSingletonClass): TSingletonHandle;
  96. // Register a new Singleton class and allocate space for the instance pointer
  97. var
  98.   Index: integer;
  99. begin
  100.   Assert(Assigned(aSingletonClass));
  101.   Assert(SingletonClasses.IndexOf(Pointer(aSingletonClass)) < 0);
  102.   SingletonClasses.Add(Pointer(aSingletonClass));
  103.   // Return the index +1 of the instace pointer as a handle
  104.   Index := SingletonInstances.Add(nil);
  105.   Result := TSingletonHandle(Index+1);
  106.   Assert(SingletonClasses.Count = SingletonInstances.Count);
  107. end;
  108.  
  109. class procedure TSingleton.OverrideSingletonClass(BaseSingletonClass, NewSingletonClass: TSingletonClass);
  110. // Allow change of instance class
  111. var
  112.   ThisClass: TSingletonClass;
  113.   i : integer;
  114. begin
  115.   Assert(Assigned(BaseSingletonClass));
  116.   Assert(Assigned(NewSingletonClass));
  117.   Assert(BaseSingletonClass <> TSingleton);
  118.   Assert(NewSingletonClass.InheritsFrom(BaseSingletonClass));
  119.   for i := 0 to SingletonClasses.Count-1 do
  120.   begin
  121.     ThisClass := TSingletonClass(SingletonClasses.List^[i]);
  122.     if ThisClass.InheritsFrom(BaseSingletonClass) and
  123.        (SingletonInstances.List^[i] = nil)        then
  124.     begin
  125.       SingletonClasses.List^[i] := Pointer(NewSingletonClass);
  126.       Exit;
  127.     end;
  128.   end;
  129.   // If we get, here the base class was not found or
  130.   // an instance had already been created
  131.   SingletonError;
  132. end;
  133.  
  134. class function TSingleton.InstanceOf(Handle: TSingletonHandle): TSingleton;
  135. // Single Instance function - create when first needed
  136. var
  137.   Index: Integer;
  138. begin
  139.   // Convert the handle back to an index - subtract 1
  140.   Index := Integer(Handle) - 1;
  141.   Assert((Index >= 0) and (Index <= SingletonInstances.Count-1));
  142.   Assert(Assigned(SingletonClasses.List^[Index]));
  143.   if not Assigned(SingletonInstances.List^[Index]) then
  144.     SingletonInstances.List^[Index] := TSingletonClass(SingletonClasses.List^[Index]).SingletonCreate;
  145.   Result := SingletonInstances.List^[Index];
  146. end;
  147.  
  148. constructor TSingleton.SingletonCreate;
  149. // Protected constructor
  150. begin
  151.   inherited Create;
  152. end;
  153.  
  154. destructor TSingleton.SingletonDestroy;
  155. // Protected destructor
  156. begin
  157.   // We cannot call inherited Destroy; here!
  158.   // It would raise an ESingleton exception
  159. end;
  160.  
  161. // Protected against use of default constructor
  162. class procedure TSingleton.Create;
  163. begin
  164.   SingletonError;
  165. end;
  166.  
  167. // Protected against use of Free
  168. class procedure TSingleton.Free(Dummy: integer);
  169. begin
  170.   SingletonError;
  171. end;
  172.  
  173. // Protected against use of default destructor
  174. class procedure TSingleton.Destroy(Dummy: integer);
  175. begin
  176.   SingletonError;
  177. end;
  178.  
  179. initialization
  180.   TSingleton.Startup;
  181. finalization
  182.   TSingleton.Shutdown;
  183. end.